package com.leyou.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.common.enums.ExceptionEnum;
import com.leyou.common.exceptions.LyException;
import com.leyou.common.threadlocals.UserHolder;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.utils.IdWorker;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.order.dto.CartDTO;
import com.leyou.order.dto.OrderDTO;
import com.leyou.order.entity.TbOrder;
import com.leyou.order.entity.TbOrderDetail;
import com.leyou.order.entity.TbOrderLogistics;
import com.leyou.order.entity.TbOrderSeckillDetail;
import com.leyou.order.enums.BusinessTypeEnum;
import com.leyou.order.enums.OrderStatusEnum;
import com.leyou.order.mapper.TbOrderMapper;
import com.leyou.order.service.TbOrderDetailService;
import com.leyou.order.service.TbOrderLogisticsService;
import com.leyou.order.service.TbOrderSeckillDetailService;
import com.leyou.order.service.TbOrderService;
import com.leyou.order.utils.PayHelper;
import com.leyou.order.vo.OrderVo;
import com.leyou.seckill.client.SecKillClient;
import com.leyou.seckill.dto.SecKillOrderDTO;
import com.leyou.seckill.dto.SeckillPolicyDTO;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.UserAddressDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author HM
 * @since 2020-10-07
 */
@Slf4j
@Service
@SuppressWarnings("{all}")
public class TbOrderServiceImpl extends ServiceImpl<TbOrderMapper, TbOrder> implements TbOrderService {

    @Autowired
    private IdWorker idWorker;
    @Autowired
    private ItemClient itemClient;
    @Autowired
    private UserClient userClient;
    @Autowired
    private TbOrderLogisticsService tbOrderLogisticsService;
    @Autowired
    private TbOrderDetailService tbOrderDetailService;
    /**
     * 创建订单
     * 操作4张表
     * 订单的相关3张表，商品表sku
     * @param orderDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createOrder(OrderDTO orderDTO) {
//        获取用户id
        Long userId = UserHolder.getUserId();
//        使用雪花算法，生成不重复的订单ID
        /*
        * 不使用UUID是因为32位的字符串,太长了,做主键不合适
        * 不使用时间戳,是因为会可能有重复,毫秒值17位,再加上3位随机数,
        * 雪花算法19位long型的数据
        *
        * */
        long orderId = idWorker.nextId();
//        获取用户提交的skuid集合
        List<CartDTO> cartDTOList = orderDTO.getCarts();
        List<Long> skuIds = cartDTOList.stream().map(CartDTO::getSkuId).collect(Collectors.toList());
//        生成skuid 和num的map  key - skuid  val - num
        Map<Long, Integer> skuIdNumMap = cartDTOList.stream().collect(Collectors.toMap(CartDTO::getSkuId, CartDTO::getNum));
//        根据skuid的集合，远程调用item服务，获取商品列表
        List<SkuDTO> skuDTOList = itemClient.findSkuListByIds(skuIds);
//        当前订单商品的总金额
        long totalFee = 0;
        List<TbOrderDetail> tbOrderDetailList = new ArrayList<>();
//        计算商品总金额  商品单价 * 数量
        for (SkuDTO skuDTO : skuDTOList) {
            Long price = skuDTO.getPrice();
            Long skuId = skuDTO.getId();
//            获取数量
            Integer num = skuIdNumMap.get(skuId);
            if(num == null || num<1){
                continue;
            }
            long total = price.longValue() * num.intValue();
//            当前商品的金额 叠加到 订单的商品总金额
            totalFee += total;
            TbOrderDetail orderDetail = new TbOrderDetail();
            orderDetail.setOrderId(orderId);
            orderDetail.setTitle(skuDTO.getTitle());
            orderDetail.setPrice(price);
            orderDetail.setOwnSpec(skuDTO.getOwnSpec());
            orderDetail.setImage(StringUtils.substringBefore(skuDTO.getImages(),","));
            orderDetail.setNum(num);
            orderDetail.setSkuId(skuId);
            tbOrderDetailList.add(orderDetail);
        }
//        运费,包邮
        long postFee = 0;
//        优惠金额, 活动 券
        long promotion = 0;
//        计算订单实付金额   实付金额=商品总金额 + 运费 - 优惠金额
        long actualFee  =  totalFee + postFee - promotion;
//        保存订单表
        TbOrder tbOrder = new TbOrder();
        tbOrder.setOrderId(orderId);
        tbOrder.setUserId(userId);
        tbOrder.setSourceType(2);
        tbOrder.setTotalFee(totalFee);
        tbOrder.setActualFee(actualFee);
        tbOrder.setPostFee(postFee);
        tbOrder.setBType(BusinessTypeEnum.MALL.value());//1=商城
        tbOrder.setStatus(OrderStatusEnum.INIT.value());
        tbOrder.setPaymentType(orderDTO.getPaymentType());
        boolean b = this.save(tbOrder);
        if(!b){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
//        根据收人信息id，远程调用user服务，获取收货人信息
        UserAddressDTO userAddressDTO = userClient.queryAddressById(orderDTO.getAddressId());
        if(userAddressDTO == null){
            throw new LyException(ExceptionEnum.ADDRESS_NOT_FOUND);
        }
        TbOrderLogistics tbOrderLogistics = BeanHelper.copyProperties(userAddressDTO,TbOrderLogistics.class);
//        设置orderid
        tbOrderLogistics.setOrderId(orderId);
//        保存订单物流表
        boolean b1 = tbOrderLogisticsService.save(tbOrderLogistics);
        if(!b1){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }





//        保存订单详情表,订单表和订单商品详情表 是1 对多关系
        boolean b2 = tbOrderDetailService.saveBatch(tbOrderDetailList);
        if(!b2){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
//       远程调用item服务，操作sku表，减库存
        try{
            itemClient.minusStock(skuIdNumMap);
        }catch(Exception e){
            e.printStackTrace();
            throw new LyException(ExceptionEnum.STOCK_NO_ENOUGH);
        }

        //清理订单的解决方案:
        // 一: 使用rocketMq,只能一个一个的清理
        //二: 使用定时任务,可以同时清理多个订单


        return orderId;
    }

    @Autowired
    private PayHelper payHelper;
    @Autowired
    private StringRedisTemplate redisTemplate;

    private String PRE_FIX = "ly:pay:orderid:";

    /**
     * 到微信支付获取支付codeurl ，有效期是2小时
     * @param orderId
     * @return
     */
    @Override
    public String getCodeUrl(Long orderId) {
        String redisKey = PRE_FIX + orderId;
//      获取缓存中的codeurl
        String cacheCode = redisTemplate.opsForValue().get(redisKey);
        if(!StringUtils.isBlank(cacheCode)){
            return cacheCode;
        }
        String body = "乐优商城支付";
//        获取订单支付金额
        TbOrder tbOrder = getById(orderId);
        if(tbOrder == null){
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        //TODO 测试，只支付1分钱
        Long actualFee = 1L;//tbOrder.getActualFee();
        String codeUrl = payHelper.unifiedorder(body, orderId, actualFee);
//        设置codeurl的缓存

        redisTemplate.opsForValue().set(redisKey,codeUrl,2, TimeUnit.HOURS);
        return codeUrl;
    }

    /**
     * 微信支付成功修改订单状态
     * 保证当前接口方法是幂等的
     * @param reqData
     */
    @Override
    public void updateOrderStatus(Map<String, String> reqData) {
        try{
//        验证微信支付的通知参数
            payHelper.checkResultCode(reqData);
//        验证请求的签名
            payHelper.isValidSign(reqData);
        }catch(Exception e){
            e.printStackTrace();
            throw new RuntimeException("【微信支付】微信支付业务失败");
        }

//        获取微信支付对应的本系统订单号
        String outTradeNo = reqData.get("out_trade_no");
        if(StringUtils.isBlank(outTradeNo)){
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }
        long orderId = Long.valueOf(outTradeNo);
//        获取订单信息
        TbOrder tbOrder = getById(orderId);
        if(tbOrder == null){
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }
//        获取用户支付金额
        String totalFeeStr = reqData.get("total_fee");
        if(StringUtils.isBlank(totalFeeStr)){
            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }
        long totalFee = Long.valueOf(totalFeeStr);
//      获取订单的支付金额
        long actualFee = tbOrder.getActualFee().longValue();
//        比较用户的支付金额和订单的实付金额是否一致
        if(totalFee != actualFee){
            log.error("用户的支付金额与订单的实付金额不一致，orderId:{}",orderId);
//            TODO 正在测试不能验证
//            throw new LyException(ExceptionEnum.INVALID_NOTIFY_PARAM);
        }

//        修改订单的状态，改为已支付  1 -> 2 ,保证幂等
//        update tb_order set status =2 where orderid=? and status=1
        UpdateWrapper<TbOrder> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().
                eq(TbOrder::getOrderId,orderId).
                eq(TbOrder::getStatus,OrderStatusEnum.INIT.value()).
                set(TbOrder::getStatus,OrderStatusEnum.PAY_UP.value());
        boolean b = update(updateWrapper);
        if(!b){
            log.error("更新订单状态到已支付失败！orderid:{}",orderId);
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }
    }


    /**
     * 关闭过期未支付订单
     * @param overTime 过期时间点
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void closeOverTimeOrder(String overTime){
//        1、根据overTime查询复合关闭条件的订单, 未支付、超时
//        select * from tb_order where status=1 and create_time<=overTime
        QueryWrapper<TbOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().
                eq(TbOrder::getStatus,OrderStatusEnum.INIT.value()).
                le(TbOrder::getCreateTime,overTime);
        List<TbOrder> tbOrderList = list(queryWrapper);
        if(CollectionUtils.isEmpty(tbOrderList)){
            return ;
        }
//        2、获取超时订单的id集合，关闭订单
        List<Long> orderIds = tbOrderList.stream().map(TbOrder::getOrderId).collect(Collectors.toList());
//        把订单状态从1 改成5
//        update tb_order set  status=5 where order_id in (?) and status =1;
        UpdateWrapper<TbOrder> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().
                in(TbOrder::getOrderId,orderIds).
                eq(TbOrder::getStatus,OrderStatusEnum.INIT.value()).
                set(TbOrder::getStatus,OrderStatusEnum.CLOSED.value());
        boolean b = update(updateWrapper);
        if(!b){
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }
//        3、获取超时订单的商品数量 ，[{skuid:1,num:2},{skuid:2,num:1},{skuid:1,num:3}]   ,skuid - num
//         select * from tb_order_detail where order_id in ?
        QueryWrapper<TbOrderDetail> queryWrapperDetail = new QueryWrapper<>();
        queryWrapperDetail.lambda().in(TbOrderDetail::getOrderId,orderIds);
        List<TbOrderDetail> tbOrderDetailList = tbOrderDetailService.list(queryWrapperDetail);
        Map<Long, Integer> skuIdNumMap = tbOrderDetailList.stream().
                collect(Collectors.groupingBy(TbOrderDetail::getSkuId, Collectors.summingInt(TbOrderDetail::getNum)));
//        4、恢复库存，远程调用item服务
        try{
            itemClient.plusStock(skuIdNumMap);
        }catch (Exception e){
            log.error("恢复商品库存失败！");
            e.printStackTrace();
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }

    }

    @Autowired
    private SecKillClient secKillClient;
    @Autowired
    private TbOrderSeckillDetailService tbOrderSeckillDetailService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createSecKillOrder(SecKillOrderDTO secKillOrderDTO) {
        //        获取用户id
        Long userId = secKillOrderDTO.getUserId();
//        使用雪花算法，生成不重复的订单ID
        long orderId = secKillOrderDTO.getOrderId();
//        获取秒杀的id
        Long seckillId = secKillOrderDTO.getSeckillId();
//        远程调用秒杀服务,获取秒杀的信息
        SeckillPolicyDTO seckillPolicyDTO = secKillClient.findSecKillPolicyById(seckillId);
//        当前订单商品的总金额
        long totalFee = seckillPolicyDTO.getCostPrice();
//        运费,包邮
        long postFee = 0;
//        优惠金额, 活动 券
        long promotion = 0;
//        计算订单实付金额   实付金额=商品总金额 + 运费 - 优惠金额
        long actualFee  =  totalFee + postFee - promotion;
//        保存订单表
        TbOrder tbOrder = new TbOrder();
        tbOrder.setOrderId(orderId);
        tbOrder.setUserId(userId);
        tbOrder.setSourceType(2);
        tbOrder.setTotalFee(totalFee);
        tbOrder.setActualFee(actualFee);
        tbOrder.setPostFee(postFee);
        tbOrder.setBType(BusinessTypeEnum.SEC_KILL.value());
        tbOrder.setStatus(OrderStatusEnum.INIT.value());
        tbOrder.setPaymentType(secKillOrderDTO.getPaymentType());
        boolean b = this.save(tbOrder);
        if(!b){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
//        根据收人信息id，远程调用user服务，获取收货人信息
        UserAddressDTO userAddressDTO = userClient.queryAddressByUser(secKillOrderDTO.getAddressId(),userId);
        if(userAddressDTO == null){
            throw new LyException(ExceptionEnum.ADDRESS_NOT_FOUND);
        }
        TbOrderLogistics tbOrderLogistics = BeanHelper.copyProperties(userAddressDTO,TbOrderLogistics.class);
//        设置orderid
        tbOrderLogistics.setOrderId(orderId);
//        保存订单物流表
        boolean b1 = tbOrderLogisticsService.save(tbOrderLogistics);
        if(!b1){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
        TbOrderSeckillDetail tbOrderSeckillDetail = new TbOrderSeckillDetail();
        tbOrderSeckillDetail.setSeckillId(seckillId);
        tbOrderSeckillDetail.setOrderId(orderId);
        tbOrderSeckillDetail.setNum(1);
//        保存订单详情表,订单表和订单商品详情表 是1 对多关系
        boolean b2 = tbOrderSeckillDetailService.save(tbOrderSeckillDetail);
        if(!b2){
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }
//       远程调用seckill服务，减库存
        try{
            secKillClient.minusStock(seckillId,1);
        }catch(Exception e){
            e.printStackTrace();
            throw new LyException(ExceptionEnum.STOCK_NO_ENOUGH);
        }
    }

    /**
     * 根据id 查询订单信息
     * @param orderId
     * @return
     */
    @Override
    public OrderVo findOrderByOrderId(Long orderId) {
        Long userId = UserHolder.getUserId();
        Integer status = OrderStatusEnum.INIT.value();
        QueryWrapper<TbOrder> orderQueryWrapper = new QueryWrapper<>();
        orderQueryWrapper.lambda().eq(TbOrder::getOrderId,orderId)
                .eq(TbOrder::getUserId,userId)
                .eq(TbOrder::getStatus,status);
        TbOrder tbOrder = this.getOne(orderQueryWrapper);
        if (tbOrder == null) {
            // 不存在
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        // 4.封装数据
        OrderVo orderVO = BeanHelper.copyProperties(tbOrder, OrderVo.class);
        return orderVO;
    }
}
